iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0

現在畫面上還未處理的區塊就剩下未來一星期的天氣還沒做了,這篇文章將會來示範如何處理這個區塊。

設定 API

在 Day 24 的文章中有提到,按照 OpenWeatherMap 的文件指示,要取得每日天氣的話,可以使用來自 daily 的資料,這是一個陣列,裡面包含了未來一星期的天氣預報,而每個物件都包含了一天的天氣資料,其中包含了最高溫、最低溫、天氣狀態等等。

所以我們只要確保 API 的 exclude 參數不包含 daily,就可以取得未來一星期的天氣預報了,就像這樣:

https://api.openweathermap.org/data/3.0/onecall?lat=25.042435&lon=121.513878&exclude=minutely,alert&units=metric&appid={APIKEI}

設定狀態

型別的部分就像這樣:

interface WeatherDetail {
  description: string
  icon: string
  id: number
  main: string
}

export interface DailyWeatherInfo {
  dt: number
  temp: {
    max: number
    min: number
  }
  weather: WeatherDetail[],
  readableDay: string
}

interface Store {
  dailyWeather: DailyWeatherInfo[] | null
}

HourlyWeatherInfo 很像,差別在於 temp 的部分,因為 daily 的資料中,temp 是一個物件,裡面包含了 maxmin,分別代表當天的最高溫跟最低溫。

然後 Store 的部分:

const convertDailyToReadableDay = (dailyData: DailyWeatherInfo[]) => {
  return dailyData.map((day: DailyWeatherInfo) => {
    const readableDay = dayjs.unix(day.dt).format('dddd')
    return { ...day, readableDay }
  })
}

// 省略其他程式碼

const useStore = create<Store>((set) => ({
  // 省略其他程式碼
  dailyWeather: null,
  fetchWeatherData: async () => {
    const { location, errorMsg } = useStore.getState()
    if (location && location.coords) {
      const { latitude, longitude } = location.coords
      const apiUrl = process.env.EXPO_PUBLIC_WEATHER_API_URL
      const apiKey = process.env.EXPO_PUBLIC_WEATHER_API_KEY
      try {
        const response = await fetch(
          `${apiUrl}lat=${latitude}&lon=${longitude}&exclude=minutely,alert&units=metric&appid=${apiKey}`
        )
        const json = await response.json()
        set({
          currentWeather: json.current,
          hourlyWeather: convertHourlyToReadableTime(json.hourly),
          dailyWeather: convertDailyToReadableDay(
            json.daily.map((day: DailyWeatherInfo) => ({
              dt: day.dt,
              temp: {
                max: day.temp.max,
                min: day.temp.min
              },
              weather: day.weather
            }))
          )
        })
      } catch (err) {
        console.log(err)
      }
    }
  },
}))

需要一個 convertDailyToReadableDay() 來將時間戳記轉換成可讀的時間,然後在 fetchWeatherData 中,將 daily 的資料轉換成我們需要的格式。

處理畫面

接著到 App.tsx 中,將 dailyWeather 的資料取出來,並且使用 FlatList 來展示:

import { useEffect, memo } from 'react'
import {
  View,
  SafeAreaView,
  FlatList,
  useWindowDimensions
} from 'react-native'
import { StatusBar } from 'expo-status-bar'
import * as Location from 'expo-location'
import useStore from './src/store'
import {
  CurrentWeather,
  DailyForecast,
  HourlyForecast,
  HeaderInfo
} from './src/components'
import { DailyWeatherInfo } from './src/store'

export default function App() {
  const {
    location,
    errorMsg,
    setLocation,
    setErrorMsg,
    fetchWeatherData,
    fetchLocation,
    hourlyWeather,
    dailyWeather
  } = useStore()

  // 省略其他程式碼

  const renderDailyItem = ({ item }: { item: DailyWeatherInfo }) => {
    const { temp, readableDay } = item
    const tempHigh = Math.round(temp.max).toString()
    const tempLow = Math.round(temp.min).toString()

    return (
      <DailyForecast
        day={readableDay}
        icon='sunny'
        tempHigh={tempHigh}
        tempLow={tempLow}
      />
    )
  }

  return (
    <SafeAreaView className='flex-1 bg-blue-500'>
      // 省略其他程式碼
      <View className='p-6'>
        <FlatList
          data={dailyWeather}
          keyExtractor={({ dt }) => dt.toString()}
          renderItem={renderDailyItem}
        />
      </View>
    </SafeAreaView>
  )
}

這裡使用 FlatList 來展示未來一星期的天氣預報,並且使用 renderDailyItem() 來處理每個項目的畫面。

畫面的部分就像這樣:

不過注意到又有時間上的問題,就是現在時間是 Tuesday,但是未來一星期又出現 Tuesday,這是因為 OpenWeatherMap API 中的日期是一次給 7 天的資料,所以我們需要在 convertDailyToReadableDay() 中處理這個問題:

const convertDailyToReadableDay = (
  dailyData: DailyWeatherInfo[],
  sliceCount: number = 7
) => {
  return dailyData.slice(1, sliceCount).map((day: DailyWeatherInfo) => {
    const readableDay = dayjs.unix(day.dt).format('dddd')
    return { ...day, readableDay }
  })
}

這樣就可以顯示不包含今天的未來一星期的天氣預報了:


上一篇
Day 25 - 使用 useWindowDimensions 讓元件自適應螢幕寬度
下一篇
Day 27 - 天氣 Icon 更新
系列文
掌握 React Native 與 Expo:30天雙打,新手也能精通跨平台開發!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言